using System;
using System.Threading;

namespace Sagile.Nepma
{
    /// <summary>
    /// This class is the base class for all tasks in the list. All tasks must inherit from TaskBase and provide a default constructor (if needed) and override the <see cref="Process"/> method.
    /// </summary>
    public class TaskBase : ISequenceTask, IComparable
    {
        #region Attributes

        private string _name;
        private bool _isMilestone;
        private bool _canBeParallelized;
        private int _parallelizationGroup;

        /// <summary>
        /// Task unique id
        /// </summary>
        private string _uniqueId;

        /// <summary>
        /// Task unique id
        /// </summary>
        private string _id;

        private Thread _worker = null;

        /// <summary>
        /// Suffix added to id to form a unique id
        /// </summary>
        private static int _uniqueCounter = 0;

        #endregion Attributes

        #region Events

        /// <summary>
        /// Raises the <see cref="Finished"/> event.
        /// </summary>
        /// <param name="e">The <see cref="ManageableTaskEventArgs"/> instance containing the event data.</param>
        protected virtual void OnFinished(ManageableTaskEventArgs e)
        {
            if (null != Finished)
                Finished(this, e);
        }

        /// <summary>
        /// Raises the <see cref="Aborted"/> event.
        /// </summary>
        /// <param name="e">The <see cref="ManageableTaskEventArgs"/> instance containing the event data.</param>
        protected virtual void OnAborted(ManageableTaskEventArgs e)
        {
            if (null != Aborted)
                Aborted(this, e);
        }

        #endregion Events

        #region ISequenceTask Members

        /// <summary>
        /// Gets or sets a value indicating whether this instance is a milestone.
        /// </summary>
        /// <value>
        /// 	<c>true</c> if this instance is a milestone; otherwise, <c>false</c>.
        /// </value>
        public bool IsMilestone
        {
            get { return _isMilestone; }
            set { _isMilestone = value; }
        }

        /// <summary>
        /// Gets or sets a value indicating whether this instance can be parallelized.
        /// </summary>
        /// <value>
        /// 	<c>true</c> if this instance can be parallelized; otherwise, <c>false</c>.
        /// </value>
        public bool CanBeParallelized
        {
            get { return _canBeParallelized; }
            set { _canBeParallelized = value; }
        }

        /// <summary>
        /// Gets or sets the parallelization group.
        /// </summary>
        /// <value>The parallelization group.</value>
        public int ParallelizationGroup
        {
            get { return _parallelizationGroup; }
            set { _parallelizationGroup = value; }
        }

        /// <summary>
        /// Gets the unique id.
        /// </summary>
        /// <value>The unique id.</value>
        public string UniqueId
        {
            get { return _uniqueId; }
        }

        /// <summary>
        /// Task id
        /// </summary>
        public string Id
        {
            get { return _id; }
            set
            {
                _id = value;
                _uniqueId = _id + ++_uniqueCounter;
            }
        }



        #endregion

        #region IManageableTask Members

        /// <summary>
        /// Occurs when the task is finished
        /// </summary>
        public event EventHandler<ManageableTaskEventArgs> Finished;

        /// <summary>
        /// Occurs when the task is aborted
        /// </summary>
        public event EventHandler<ManageableTaskEventArgs> Aborted;

        /// <summary>
        /// Gets or sets the name of the task.
        /// </summary>
        /// <value>The name.</value>
        public string Name
        {
            get { return _name; }
            set { _name = value; }
        }

        /// <summary>
        /// Run the task.
        /// </summary>
        public virtual void Run()
        {
            _worker = new Thread(SafeRun);
            _worker.Start();
        }

        /// <summary>
        /// Executes the process and catches all exceptions to raise a corresponding event.
        /// </summary>
        private void SafeRun()
        {
            try
            {
                Process();
            }
            catch (Exception e)
            {
                OnTaskExceptionThrown(new TaskExceptionThrownEventArgs(UniqueId, e));
            }
        }

        /// <summary>
        /// Stops the task.
        /// </summary>
        public virtual void Stop()
        {
            OnAborted(new ManageableTaskEventArgs(UniqueId, Name, "Stopped"));
        }

        #endregion

        #region Process

        /// <summary>
        /// Method called by the worker thread. Finished event is called at the end of the method.
        /// </summary>
        protected virtual void Process()
        {
            OnFinished(new ManageableTaskEventArgs(UniqueId, Name, String.Empty));
        }

        #endregion Process

        #region Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="TaskBase"/> class.
        /// </summary>
        /// <param name="taskName">Name of the task.</param>
        public TaskBase(string taskName)
        {
            _name = taskName;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="TaskBase"/> class.
        /// </summary>
        /// <param name="taskName">Name of the task.</param>
        /// <param name="isMilestone">if set to <c>true</c> [is milestone].</param>
        public TaskBase(string taskName, bool isMilestone)
            : this(taskName)
        {
            _isMilestone = isMilestone;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="TaskBase"/> class.
        /// </summary>
        /// <param name="taskName">Name of the task.</param>
        /// <param name="canBeParallelized">if set to <c>true</c> [can be parallelized].</param>
        /// <param name="parallelizationGroup">The parallelization group.</param>
        public TaskBase(string taskName, bool canBeParallelized, int parallelizationGroup)
            : this(taskName)
        {
            _canBeParallelized = canBeParallelized;
            _parallelizationGroup = parallelizationGroup;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="TaskBase"/> class.
        /// </summary>
        /// <param name="taskName">Name of the task.</param>
        /// <param name="isMilestone">if set to <c>true</c> [is milestone].</param>
        /// <param name="canBeParallelized">if set to <c>true</c> [can be parallelized].</param>
        /// <param name="parallelizationGroup">The parallelization group.</param>
        public TaskBase(string taskName, bool isMilestone, bool canBeParallelized, int parallelizationGroup)
            : this(taskName, canBeParallelized, parallelizationGroup)
        {
            _isMilestone = isMilestone;
        }

        #endregion Constructors

        #region ToString

        /// <summary>
        /// Returns a <see cref="T:System.String"></see> that represents the current <see cref="T:System.Object"></see>.
        /// </summary>
        /// <returns>
        /// A <see cref="T:System.String"></see> that represents the current <see cref="T:System.Object"></see>.
        /// </returns>
        public override string ToString()
        {
            return String.Format("{0} | {1}milestone | {2}parallelizable | group {3}",
                                 Name, IsMilestone ? String.Empty : "not ",
                                 CanBeParallelized ? String.Empty : "not ", ParallelizationGroup);
        }

        #endregion ToString()

        #region IComparable Members

        /// <summary>
        /// Compares the current instance with another object of the same type.
        /// </summary>
        /// <param name="obj">An object to compare with this instance.</param>
        /// <returns>
        /// A 32-bit signed integer that indicates the relative order of the objects being compared. The return value has these meanings: Value Meaning Less than zero This instance is less than obj. Zero This instance is equal to obj. Greater than zero This instance is greater than obj.
        /// </returns>
        /// <exception cref="T:System.ArgumentException">obj is not the same type as this instance. </exception>
        public int CompareTo(object obj)
        {
            TaskBase t = obj as TaskBase;
            if (null == t)
                throw new ArgumentException("obj is not a TaskBase");

            int compareGroup = ParallelizationGroup.CompareTo(t.ParallelizationGroup);

            if (compareGroup == 0)
            {
                // Not parallelizable MUST be executed AFTER parallelizable
                return t.CanBeParallelized.CompareTo(CanBeParallelized);
            }
            else
            {
                return compareGroup;
            }
        }

        #endregion

        #region IProgressReporter Members

        /// <summary>
        /// Occurs when the current progress state is changed
        /// </summary>
        public event EventHandler<TaskProgressChangedEventArgs> TaskProgressChanged;

        /// <summary>
        /// Raises the <see cref="TaskProgressChanged"/> event.
        /// </summary>
        /// <param name="e">The <see cref="TaskProgressChangedEventArgs"/> instance containing the event data.</param>
        protected virtual void OnTaskProgressChanged(TaskProgressChangedEventArgs e)
        {
            if (null != TaskProgressChanged)
                TaskProgressChanged(this, e);
        }

        /// <summary>
        /// Called when the current progress state is changed.
        /// </summary>
        /// <param name="progress">The progress state.</param>
        protected virtual void OnTaskProgressChanged(double progress)
        {
            OnTaskProgressChanged(new TaskProgressChangedEventArgs(UniqueId, progress));
        }

        #endregion

        #region IExceptionReporter Members

        /// <summary>
        /// Occurs when an exception is raised during task processing
        /// </summary>
        public event EventHandler<TaskExceptionThrownEventArgs> TaskExceptionThrown;

        /// <summary>
        /// Raises the <see cref="TaskExceptionThrown"/> event.
        /// </summary>
        /// <param name="e">The <see cref="TaskExceptionThrownEventArgs"/> instance containing the event data.</param>
        protected virtual void OnTaskExceptionThrown(TaskExceptionThrownEventArgs e)
        {
            if (null != TaskExceptionThrown)
                TaskExceptionThrown(this, e);
        }

        #endregion
    }
}
